/*
 * This code is made available under version 3 of the
 * GNU GENERAL PUBLIC LICENSE. See the file LICENSE in this
 * distribution for details.
 * 
 * Copyright 2008 Eric Bodden
 */

package deadlock;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.reflect.SourceLocation;

/**
 * Locking aspect, keeping track of thread-local lock sets.
 * 
 * @author Eric Bodden
 */
public aspect Deadlock {
	public static Bag resources=new HashBag();
	public static Bag threads=new HashBag();
	public static Bag availRes=new HashBag();
	public static Map allocation=new HashMap();
	public static Map request=new HashMap();
	public static Object checking=new Object();

/*	ThreadLocal allocation = new ThreadLocal() {
		 protected synchronized Object initialValue() {
			 return new HashBag();
		 }
	};
	ThreadLocal request = new ThreadLocal() {
		 protected synchronized Object initialValue() {
			 return new HashBag();
		 }
	};*/
	
	before(Object l): lock() && args(l) && !within(Deadlock)&&!cflow(within(Deadlock)){
		synchronized(request)
		{
			HashBag req=(HashBag) request.get(Thread.currentThread().getName());
			if(req==null)
			{
				HashBag newReq=new HashBag();
				newReq.add(l);
				request.put(Thread.currentThread().getName(), newReq);
			}
			else
			{
				req.add(l);
				request.put(Thread.currentThread().getName(), req);
			}
		}
		synchronized(resources)
		{
	        resources.add(l);
		}
		synchronized(threads)
		{
	        threads.add(Thread.currentThread().getName());
		}
		synchronized(availRes)
		{
			if(!resources.contains(l))availRes.add(l);
		}
		if(isDeadlock())
		{
			
			String id = getId(thisJoinPointStaticPart);
			Class owner = thisJoinPointStaticPart.getSignature().getDeclaringType();		
			SourceLocation loc = location(thisJoinPointStaticPart);
			System.err.println("Thread "+Thread.currentThread().getName()+" Suspect by lock"+l+"!!");
			System.err.println("DeadLock:"+" "+owner+"  location:"+loc);
		}
				
		System.err.println("Thread "+
					Thread.currentThread().getName()+" waiting locks:"+l);
	}


	after(Object l): lock() && args(l)&&!within(Deadlock)&&!cflow(within(Deadlock)) {
		synchronized(request)
		{
			HashBag req=(HashBag) request.get(Thread.currentThread().getName());
			if(req!=null)
			{
				req.remove(l);
				request.put(Thread.currentThread().getName(),req);
			}
		}
		synchronized(allocation)
		{
			HashBag alloc=(HashBag) allocation.get(Thread.currentThread().getName());
			if(alloc==null)
			{
				HashBag newAlloc=new HashBag();
				newAlloc.add(l);
				allocation.put(Thread.currentThread().getName(), newAlloc);
			}
			else
			{
				alloc.add(l);
				allocation.put(Thread.currentThread().getName(), alloc);
			}
		}
		
		synchronized(availRes){
			availRes.remove(l);
		}
		
		System.err.println("Thread "+
				Thread.currentThread().getName()+" holding locks: "+l);
	    
	}


	after(Object l): unlock() && args(l)&& !within(Deadlock)&&!cflow(within(Deadlock)){
		synchronized(allocation)
		{
			HashBag alloc=(HashBag) request.get(Thread.currentThread().getName());
			if(alloc!=null)
			{
				alloc.remove(l);
				allocation.put(Thread.currentThread().getName(),alloc);
			}
		}
		synchronized(availRes)
		{
			availRes.add(l);
		}
		
		System.err.println("Thread "+
			    Thread.currentThread().getName()+" release locks: "+l);
	}

	
	private String getId(StaticPart sp) {
		return sp.getSignature().toLongString().intern();
	}
	private SourceLocation location(StaticPart sp){
		return sp.getSourceLocation();
	}
	
	
	private boolean isDeadlock() {
		// TODO Auto-generated method stub
		synchronized(checking){
	    synchronized(availRes){
	    synchronized(allocation){
	    synchronized(request)
	    {
	    	HashBag work=new HashBag();
	    	work.addAll(availRes);
		    HashBag finish=new HashBag();
		    Iterator it=threads.iterator();
		    while(it.hasNext())
		    {
		    	String thread=(String)it.next();
		    	if(allocation.get(thread)!=null){
		    		if(((HashBag)allocation.get(thread)).isEmpty()){
		    			finish.add(thread);}
		    	}
		    	if(allocation.get(thread)==null)finish.add(thread);
		    }
		    
		    int threadNum=threads.size();
		    int i=0;
		    it=threads.iterator();
		    while(it.hasNext())
		    {   
		    	String thread=(String)it.next();
		    	if(!finish.contains(thread)&&work.containsAll((HashBag)request.get(thread))){
		    		if(allocation.get(thread)!=null)
		    			work.addAll((HashBag)allocation.get(thread));
		    	    finish.add(thread);
		    		it=threads.iterator();
		    		i=0;
		    		continue;
		    	}
		    	else{
		    		i++;
		    	}
		    	if(i>=threadNum)break;
		    }

		    it=threads.iterator();
		    while(it.hasNext())
		    {
		        String thread=(String)it.next();
		        if(!finish.contains(thread))return true;
		    }
		    
	    }}}}
		return false;
	}
}
